﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleCsv
{
    public class CsvWriter : IDisposable
    {
        readonly char DelimiterChar;
        readonly char QuoteChar;
        static char[] TrimChars = CsvReader.TrimChars;

        TextWriter _writer;
        bool _leaveOpen;

        public CsvWriter(TextWriter writer,
            char delimiterChar = ',', char quoteChar = '"', bool leaveOpen = false)
        {
            if (writer == null) { throw new ArgumentException("writer"); }

            _writer = writer;
            _leaveOpen = leaveOpen;

            DelimiterChar = delimiterChar;
            QuoteChar = quoteChar;
        }

        public void WriteRow(CsvRow row)
        {
            String rowStr = EncodeRow(row);
            _writer.WriteLine(rowStr);
        }

        public void WriteRow(params object[] fields)
        {
            var row = fields.Select(x => x == null ? "" : x.ToString());
            String rowStr = EncodeRow(row, DelimiterChar, QuoteChar);
            _writer.WriteLine(rowStr);
        }

        /// <summary>
        /// Encode a single CSV line. 
        /// Note: does NOT add newline at the end.
        /// </summary>
        /// <param name="fields"></param>
        /// <returns></returns>
        public static String EncodeRow(IEnumerable<String> fields, 
            char delimiterChar = ',', char quoteChar = '"')
        {
            StringBuilder line = new StringBuilder();
            bool first = true;
            foreach (String field in fields)
            {
                if (!first) { line.Append(delimiterChar); }
                first = false;
                String csvField = EncodeCsvField(field, delimiterChar, quoteChar);
                line.Append(csvField);
            }
            return line.ToString();
        }

        /// <summary>
        /// Encode a single CSV field
        /// </summary>
        /// <param name="field"></param>
        /// <returns></returns>
        static String EncodeCsvField(String field, char delimiterChar, char quoteChar)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(field);

            // Fields with embedded commas must be delimited with double-quote characters.
            bool embedInQuotes = false;

            // Leading and trailing space-characters adjacent to comma field separators are ignored.
            if (sb.Length > 0 &&
                (sb[0] == ' ' ||
                 sb[0] == '\t' ||
                 sb[sb.Length - 1] == ' ' ||
                 sb[sb.Length - 1] == '\t')) { embedInQuotes = true; }

            for (int i = 0; i < sb.Length; i++)
            {
                // Fields with embedded commas must be delimited with double-quote characters.
                // A field that contains embedded line-breaks must be surounded by double-quotes
                // Fields that contain double quote characters must be surounded by double-quotes, 
                if (sb[i] == delimiterChar ||
                    sb[i] == quoteChar ||
                    sb[i] == '\r' ||
                    sb[i] == '\n')
                {
                    embedInQuotes = true;
                    break;
                }
            }

            // Embedded double-quotes must each be represented by a pair of consecutive 
            // double quotes.
            sb.Replace(
                "" + quoteChar,
                "" + quoteChar + quoteChar);

            if (embedInQuotes)
            {
                sb.Insert(0, quoteChar);
                sb.Append(quoteChar);
            }

            return sb.ToString();
        }

        public void Dispose()
        {
            if (_writer != null)
            {
                _writer.Flush();
                if (!_leaveOpen) { _writer.Dispose(); }
                _writer = null;
            }
        }
    }
}
